﻿//-----------------------------------------------------------------------
// <copyright>
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//----------------------------------------------------------------------- 
using System;
using System.Linq;
using System.Windows.Threading;
using Fulcrum.DispatcherProxies;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Fulcrum.UnitTests.DispatcherProxies
{
    [TestClass]
    public class DispatcherProxyTargetTests
    {
        public DispatcherProxyTargetTests()
        {
            // nothing
        }

        private static DispatcherProxyTarget CreateDispatcherObjectProxy()
        {
            var target = new DispatcherProxyTarget(typeof(DispatcherObject), null);
            return target;
        }

        [TestMethod]
        public void TestDispatcherObjectTargetProxyName()
        {
            var target = CreateDispatcherObjectProxy();

            Assert.AreEqual("DispatcherObjectProxy", target.ProxyName);
        }

        [TestMethod]
        public void TestDispatcherObjectTargetBaseProxy()
        {
            var target = CreateDispatcherObjectProxy();

            Assert.IsNull(target.BaseProxy);
        }

        [TestMethod]
        public void TestDispatcherObjectTargetIsAbstract()
        {
            var target = CreateDispatcherObjectProxy();

            Assert.IsTrue(target.IsAbstract);
        }

        [TestMethod]
        public void TestDispatcherObjectTargetWrappedType()
        {
            var target = CreateDispatcherObjectProxy();

            Assert.AreEqual(typeof(DispatcherObject).FullName, target.WrappedType);
        }

        [TestMethod]
        public void TestDispatcherObjectTargetConstructors()
        {
            var target = CreateDispatcherObjectProxy();

            var constructors = target.GetConstructors();

            Assert.AreEqual(0, constructors.Count);
        }

        [TestMethod]
        public void TestDispatcherObjectTargetEvents()
        {
            var target = CreateDispatcherObjectProxy();

            Assert.AreEqual(0, target.GetInstanceEvents().Count);
        }

        [TestMethod]
        public void TestDispatcherObjectTargetMethods()
        {
            var target = CreateDispatcherObjectProxy();

            var methods = target.GetInstanceMethods();
            Assert.AreEqual(2, methods.Count);

            Console.WriteLine("Validating first object");
            ValidateMethod(methods.ElementAt(0), "CheckAccess");

            Console.WriteLine("Validating second object");
            ValidateMethod(methods.ElementAt(1), "VerifyAccess");
        }

        [TestMethod]
        public void TestDispatcherObjectTargetProperties()
        {
            var target = CreateDispatcherObjectProxy();

            var properties = target.GetInstanceProperties();
            Assert.AreEqual(1, properties.Count);

            ValidateProperty(properties.ElementAt(0), "Dispatcher", true, false);
        }

        private static DispatcherProxyTarget CreateMyDispatcherClassProxy()
        {
            var baseProxy = CreateDispatcherObjectProxy();
            var target = new DispatcherProxyTarget(typeof(MyDispatcherClass), baseProxy);
            return target;
        }

        [TestMethod]
        public void TestMyDispatcherClassTargetProxyName()
        {
            var target = CreateMyDispatcherClassProxy();

            Assert.AreEqual("MyDispatcherClassProxy", target.ProxyName);
        }
        
        [TestMethod]
        public void TestMyDispatcherClassTargetBaseProxy()
        {
            var target = CreateMyDispatcherClassProxy();

            Assert.IsNotNull(target.BaseProxy);
        }

        [TestMethod]
        public void TestMyDispatcherClassTargetIsAbstract()
        {
            var target = CreateMyDispatcherClassProxy();

            Assert.IsFalse(target.IsAbstract);
        }

        [TestMethod]
        public void TestMyDispatcherClassTargetWrappedType()
        {
            var target = CreateMyDispatcherClassProxy();

            Assert.AreEqual(typeof(MyDispatcherClass).FullName, target.WrappedType);
        }

        [TestMethod]
        public void TestMyDispatcherClassTargetConstructors()
        {
            var target = CreateMyDispatcherClassProxy();

            var constructors = target.GetConstructors();

            Assert.AreEqual(1, constructors.Count);

            var constructor = constructors.ElementAt(0);

            ValidateConstructor(constructor, false, 0);

        }

        [TestMethod]
        public void TestMyDispatcherClassTargetEvents()
        {
            var target = CreateMyDispatcherClassProxy();

            var events = target.GetInstanceEvents();
            Assert.AreEqual(1, events.Count);

            ValidateEvent(events.ElementAt(0), "OverrideEvent");
        }

        private static void ValidateEvent(DispatcherProxyEventInfo @event, string name)
        {
            Assert.AreEqual(name, @event.Name);
        }

        [TestMethod]
        public void TestMyDispatcherClassTargetMethods()
        {
            var target = CreateMyDispatcherClassProxy();

            var methods = target.GetInstanceMethods();
            Assert.AreEqual(1, methods.Count);

            ValidateMethod(methods.ElementAt(0), "Method");
        }

        [TestMethod]
        public void TestMyDispatcherClassTargetProperties()
        {
            var target = CreateMyDispatcherClassProxy();

            var properties = target.GetInstanceProperties();
            Assert.AreEqual(1, properties.Count);

            ValidateProperty(properties.ElementAt(0), "OverrideReadProperty", true, false);
        }

        private static DispatcherProxyTarget CreateMyOtherDispatcherClassProxy()
        {
            var baseProxy = CreateMyDispatcherClassProxy();
            var target = new DispatcherProxyTarget(typeof(MyOtherDispatcherClass), baseProxy);
            return target;
        }

        [TestMethod]
        public void TestMyOtherDispatcherClassTargetConstructors()
        {
            var target = CreateMyOtherDispatcherClassProxy();

            var constructors = target.GetConstructors();

            Assert.AreEqual(2, constructors.Count);

            ValidateConstructor(constructors.ElementAt(0), true, 0);
            ValidateConstructor(constructors.ElementAt(1), false, 1);

        }

        [TestMethod]
        public void TestMyOtherDispatcherClassTargetEvents()
        {
            var target = CreateMyOtherDispatcherClassProxy();

            var events = target.GetInstanceEvents();
            Assert.AreEqual(3, events.Count);

            ValidateEvent(events.ElementAt(0), "RegularEvent");
            ValidateEvent(events.ElementAt(1), "VirtualEvent");
            ValidateEvent(events.ElementAt(2), "OverrideEvent");
        }

        [TestMethod]
        public void TestMyOtherDispatcherClassTargetMethods()
        {
            var target = CreateMyOtherDispatcherClassProxy();

            var methods = target.GetInstanceMethods();
            Assert.AreEqual(2, methods.Count);

            ValidateMethod(methods.ElementAt(0), "Method");
            ValidateMethod(methods.ElementAt(1), "Method");
        }

        [TestMethod]
        public void TestMyOtherDispatcherClassTargetProperties()
        {
            var target = CreateMyOtherDispatcherClassProxy();

            var properties = target.GetInstanceProperties();
            Assert.AreEqual(4, properties.Count);

            ValidateProperty(properties.ElementAt(0), "PublicReadWriteProperty", true, false);
            ValidateProperty(properties.ElementAt(1), "ReadProperty", true, false);
            ValidateProperty(properties.ElementAt(2), "ReadWriteProperty", true, true);
            ValidateProperty(properties.ElementAt(3), "VirtualReadProperty", true, false);
        }

        private static void ValidateMethod(DispatcherProxyMethodInfo method, string name)
        {
            Assert.AreEqual(name, method.Method.Name);
            Assert.IsFalse(method.IsNew);
        }

        private static void ValidateProperty(DispatcherProxyPropertyInfo property, string name, bool canRead, bool canWrite)
        {
            Console.WriteLine("Validating {0}", property.Name);

            Assert.AreEqual(name, property.Name);
            Assert.IsFalse(property.IsNew);
            Assert.AreEqual(canRead, property.CanRead);
            Assert.AreEqual(canWrite, property.CanWrite);
        }

        private static void ValidateConstructor(DispatcherProxyConstructorInfo constructor, bool isNew, int numParameters)
        {
            Console.WriteLine("Validating constructor {0}", constructor.Constructor);

            Assert.AreEqual(numParameters, constructor.Constructor.GetParameters().Length);
            Assert.AreEqual(isNew, constructor.IsNew);
        }

    }
}
